#include "muduo/base/Logging.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpClient.h"
#include "muduo/net/TcpServer.h"
#include <sys/time.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <cmath>

using namespace muduo;
using namespace muduo::net;

const size_t frameLen = 2*sizeof(int64_t);
int64_t g_offsetSec = 8 * 3600;  // Beijing 

/// @brief 以时间服务器角色, 接待新建立的链接
/// @param conn 
void serverConnectionCallback(const TcpConnectionPtr& conn)
{
  LOG_TRACE << conn->name() << " " << conn->peerAddress().toIpPort() << " -> "
        << conn->localAddress().toIpPort() << " is "
        << (conn->connected() ? "UP" : "DOWN");
  if (conn->connected())
  {
    conn->setTcpNoDelay(true);
  }
  else
  {
  }
}

/// @brief 以时间服务器角色, 接待客户端的消息:反馈服务器的时间
/// @param conn 
/// @param buffer 
/// @param receiveTime 
void serverMessageCallback(const TcpConnectionPtr& conn, Buffer* buffer,
                           muduo::Timestamp receiveTime) {
  int64_t message[2];
  while (buffer->readableBytes() >= frameLen) {
    memcpy(message, buffer->peek(), frameLen);
    buffer->retrieve(frameLen);
    message[1] = receiveTime.microSecondsSinceEpoch();
    conn->send(message, sizeof message);
  }
}

void runServer(uint16_t port)
{
  EventLoop loop;
  TcpServer server(&loop, InetAddress(port), "ClockServer");
  server.setConnectionCallback(serverConnectionCallback);
  server.setMessageCallback(serverMessageCallback);
  server.start();
  loop.loop();
}

/** 以下为客户端代码 */
TcpConnectionPtr clientConnection;
TimerId g_sendTiemr;
// uint32_t g_reqCount = 0;

void* sendMyTime2(void* arg) 
{
  LOG_INFO << "call sendMyTime";
  while(1) {
    // if (g_reqCount < ) {
    //   g_reqCount++;
    //   continue;
    // } else {
    //   g_reqCount = 0;
    // }
    if (clientConnection)
    {
      int64_t message[2] = { 0, 0 };
      message[0] = Timestamp::now().microSecondsSinceEpoch();
      clientConnection->send(message, sizeof message);
    }
    sleep(5);
  }
  return 0;
}

void sendMyTime()
{
  LOG_INFO << "call sendMyTime";
  if (clientConnection)
  {
    int64_t message[2] = { 0, 0 };
    message[0] = Timestamp::now().microSecondsSinceEpoch();
    clientConnection->send(message, sizeof message);
  }
}

/// @brief 配置系统时间,仅适用于相差1s的场景
/// @param ts 传入此时刻的时间戳;单位:ms
/// @return 1.配置成功;
bool setLocalTime(int64_t ts)
{
  /// FIXME:经验证, g_offsetSec=--2x8x3600, 然后再调用settimeofday配置时间,
  ///       client的UTC时间及Local时间才正确; 也就说, their的时间是已经加了
  //        8小时, 然后settimeofday()是只配置本机的UTC Time;
  struct timeval tv;
  tv.tv_sec = (ts / Timestamp::kMicroSecondsPerSecond) + g_offsetSec;
  tv.tv_usec = 0;

  if (settimeofday(&tv, NULL) == -1) {
    LOG_ERROR << "settimeofday failed";
    return false;
  }
  return true;
}

/// @brief 当客户端连上时
/// @param conn 
void clientConnectionCallback(const TcpConnectionPtr& conn)
{
  LOG_TRACE << conn->localAddress().toIpPort() << " -> "
        << conn->peerAddress().toIpPort() << " is "
        << (conn->connected() ? "UP" : "DOWN");
  if (conn->connected())
  {
    clientConnection = conn;
    conn->setTcpNoDelay(true);
  }
  else
  {
    clientConnection.reset();
  }
}

/// @brief 当客户端收到时间服务器的回调时
/// @param  
/// @param buffer 
/// @param receiveTime 
void clientMessageCallback(const TcpConnectionPtr& conn, Buffer* buffer,
                           muduo::Timestamp receiveTime) {
  int64_t message[2];
  while (buffer->readableBytes() >= frameLen) {
    memcpy(message, buffer->peek(), frameLen);
    buffer->retrieve(frameLen);
    int64_t send = message[0];
    int64_t their = message[1];
    int64_t back = receiveTime.microSecondsSinceEpoch();
    int64_t mine = (back + send) / 2; // 其实都是client一收一发的时间戳
    // LOG_INFO << send << "," << their << "," << back << "," << mine << ",";
    LOG_INFO << "round trip " << back - send << " clock error " << their - mine;
    // 1690961810701893,  1742936872682001,   1690961810703648, 1690961810702770, - mntp.cpp:106
    // 2023/8/2 15:36:51, 2025/3/26 05:07:52, 2023/8/2 15:36:51,2023/8/2 15:36:51
    // round trip 1755 clock error 51975061979231 - mntp.cpp:107

    // 若远端和本地接收时间差大于1s, 则更新时间
    /// FIXME:此处加上传输时间耗时一半
    int64_t errSec = fabs(static_cast<double>(their - mine) / Timestamp::kMicroSecondsPerSecond);
    LOG_INFO << "(errSec + g_offsetSec):" << (errSec + g_offsetSec);
    if (abs(errSec + g_offsetSec) > 1) 
    {
      int64_t nowMicroSec = their + static_cast<int64_t>(ceil(abs(back - send)/2.0));
      setLocalTime(nowMicroSec);
      LOG_INFO << "update local time:" << nowMicroSec / Timestamp::kMicroSecondsPerSecond;

      // auto loop = conn->getLoop();
      // loop->cancel(g_sendTiemr);
      // g_sendTiemr = loop->runEvery(1.0, sendMyTime);
    }
  }
}

void runClient(const char* ip, uint16_t port)
{
  pthread_t thread_id;  // 线程 ID

  // 创建线程
  if (pthread_create(&thread_id, NULL, sendMyTime2, NULL) != 0) {
      perror("pthread_create");
      return;
  }

  printf("send thread is running...\n");

  EventLoop loop;
  TcpClient client(&loop, InetAddress(ip, port), "ClockClient");
  client.enableRetry();
  client.setConnectionCallback(clientConnectionCallback);
  client.setMessageCallback(clientMessageCallback);
  client.connect();
  // g_sendTiemr = loop.runEvery(1.0, sendMyTime);
  loop.loop();

  // 等待线程结束
  if (pthread_join(thread_id, NULL) != 0) {
    perror("pthread_join");
    // return 1;
  }

  printf("Main thread is done.\n");
}


int main(int argc, char* argv[])
{
  if (argc > 2)
  {
    uint16_t port = static_cast<uint16_t>(atoi(argv[2]));
    if (strcmp(argv[1], "-s") == 0)
    {
      runServer(port);
    }
    else
    {
      if (argc > 4 && strcmp(argv[3], "-o") == 0) {
        g_offsetSec = atol(argv[4]);
      }
      runClient(argv[1], port);
    }
  }
  else
  {
    printf("Usage:\n"
      "As a server:%s -s port(124)\n"
      "As a client:%s ip port(124) -o (offset_sec)\n",
      argv[0], argv[0]);
  }
}